<template>
	<div class="w-full h-full">
		<div id="chat-container" class="flex h-full">
			<!-- 左侧布局 -->
			<!-- 小屏适配抽屉菜单 -->
			<div v-if="commonStore.isSmallScreen" class = "border-r">
				<el-drawer direction="ltr" title="聊天管理" size="50%" v-model="isOpenNav">
				    <template #default>
				      <div class="mx-2 my-2 text-center">
				      	<el-button type="primary" class="w-full" @click="createNewSession()" truncated>新建聊天+</el-button>
				      </div>
				      <div class="px-2 py-2 mx-2 my-2 relative session-item" v-for="(item) in chatSessions" :class="{'session-active': data.sessionId==item.sessionId}" @click="changeSession(item.sessionId)">
				      	<div class="truncate">{{item.text}}</div>
				      	<div class="text-xs text-slate-400 " truncated>{{item.time}}</div>
				      	<div class="absolute bottom-1 right-2" @click="deleteSession(item.sessionId)"><el-icon><Delete style="width: 1rem; height: 1rem;" /></el-icon></div>
				      </div>
				    </template>
					<template #footer>
					     <div class="flex items-center justify-center">
					     	<div class="">
					     		<el-avatar :size="40" :src="data.modelInfo.avatarUrl" style="background-color: transparent"/>
					     	</div>
					     	<div class="px-2">
					     		<strong>智谱清言</strong>
					     	</div>
					    </div>
					</template>
				  </el-drawer>
				  <!-- 折叠菜单 -->
				  <div class="expand-btn" @click="isOpenNav = true">
					  <el-icon :size="25" class="pl-2"><ArrowRight /></el-icon>
				  </div>
			</div>
		
			<div class="h-full flex flex-col border-r w-[12rem]" v-else>
				<!-- 会话列表 -->
				<div class="flex-1">
					<div class="mx-2 my-2 text-center">
						<el-button type="primary" class="w-full" @click="createNewSession()" truncated>新建聊天+</el-button>
					</div>
					<div class="px-2 py-2 mx-2 my-2 relative session-item" v-for="(item) in chatSessions" :class="{'session-active': data.sessionId==item.sessionId}" @click="changeSession(item.sessionId)">
						<div class="truncate">{{item.text}}</div>
						<div class="text-xs text-slate-400 " truncated>{{item.time}}</div>
						<div class="absolute bottom-1 right-2 hidden hover-show" @click="deleteSession(item.sessionId)"><el-icon><Delete style="width: 1rem; height: 1rem;" /></el-icon></div>
					</div>
				</div>
				<!-- 模型名称 -->
				<div class="py-4 px-2">
					<div class="flex items-center justify-center">
						<div class="">
							<el-avatar :size="30" :src="data.modelInfo.avatarUrl" style="background-color: transparent"/>
						</div>
						<div class="pl-2">
							<strong>{{data.modelInfo.label}}</strong>
						</div>
						<!-- 设置 -->
						<div class="pl-2 flex items-center">
							<el-icon size="20" class="hover-primary" @click="settingData.dialogShow=true"><Setting/></el-icon>
						</div>
					</div>
				</div>
			</div>
			<!-- 右侧布局 -->
			<div class="flex flex-1 justify-center" style="width: 0">
				<div class="flex flex-col max-w-5xl w-full h-full py-4 px-4">
					<!-- 聊天记录 -->
					<div id="chat-history-container" class="flex-1 w-full overflow-y-auto mb-4"
						ref="chatHistoryContainerRef">
						<div v-if="chatHistory.length==0" class="flex flex-col h-full justify-center mx-4">
							<div><el-text size="large">您可以尝试：</el-text></div>
							<div class="mt-2" v-for="(item) in data.example">
								<el-button class="w-full" @click="submitExample($event)">{{item}}</el-button>
							</div>
						</div>
						<!-- 单条对话消息 -->
						<div v-for="(item) in chatHistory">
							<!-- 信息来源 -->
							<div class="flex">
								<!-- 头像 -->
								<div>
									<el-avatar :size="20" :src="item.source==0 ? data.userInfo.avatarUrl:data.modelInfo.avatarUrl" style="background-color: transparent"/>
								</div>
								<!-- 名称 -->
								<div class="flex items-center px-2">
									<el-text v-if="item.source==0" tag="b">{{data.userInfo.label}}</el-text>
									<el-text v-else tag="b">{{data.modelInfo.label}}</el-text>
								</div>
							</div>
							<!-- 消息内容 -->
							<div class="overflow-x-auto">
								<v-md-preview :text="item.text" style="padding: 0.5rem 0;"
									@copy-code-success="handleCopyCodeSuccess" class=""></v-md-preview>
							</div>
						</div>
					</div>
					<!-- 输入框 -->
					<div class="flex mb-4">
						<el-input v-model="inputContent" class="flex-1 ml-4" type="text" placeholder="来说点什么吧..."
							ref="inputRef" @keyup.enter="submitMessage(inputContent)" />
						<el-button size="large" type="primary" class="px-4 py-2 mx-4"
							@click="submitMessage(inputContent)" :loading="waitModelReply">提交</el-button>
					</div>
				</div>
			</div>
		</div>
		<el-dialog
		    v-model="settingData.dialogShow"
		    title="设置"
		    width="500">
		    <el-form :model="settingData.form" label-width="auto" style="max-width: 600px">
				 <el-form-item label="服务商">
				    <el-select v-model="settingData.form.vendor" placeholder="请选择" @change="changeVendor()">
				    	<el-option :key="item.name" :label="item.label" :value="item.name" :default="index==0" v-for="(item, index) in settingData.vendorOptions">
							<div class="flex items-center">
								<el-avatar :size="20" :src="item.avatarUrl" style="background-color: transparent"/>
								<div class="ml-1">{{item.label}}</div>
							</div>
						</el-option>
				    </el-select>
				</el-form-item>
				<el-form-item label="API密钥" v-if="settingData.form.vendor=='zpqy'">
				    <el-input v-model="settingData.form.modelConfig.zpqy.secretKey" />
				</el-form-item>
				<el-form-item label="APPID" v-if="settingData.form.vendor=='tyqw'">
				    <el-input v-model="settingData.form.modelConfig.tyqw.appId" />
				</el-form-item>
				<el-form-item label="APP密钥" v-if="settingData.form.vendor=='tyqw'">
				    <el-input v-model="settingData.form.modelConfig.tyqw.appSecret" />
				</el-form-item>
				<el-form-item label="模型名称">
				    <el-select v-model="settingData.form.modelConfig[settingData.form.vendor].modelName" placeholder="请选择" >
				    	<el-option :label="item" :value="item" v-for="(item, index) in settingData.modelOptions[settingData.form.vendor]" :default="index==0"/>
				    </el-select>
				</el-form-item>
			</el-form>
		    <template #footer>
		      <div class="dialog-footer">
		        <el-button @click="settingData.dialogShow = false">取消</el-button>
		        <el-button type="primary" @click="submitSetting()">
		          确定
		        </el-button>
		      </div>
		    </template>
		  </el-dialog>
	</div>
</template>

<script lang="ts" setup>
	import { reactive, ref, onMounted, onUnmounted } from 'vue'
	import { ElMessage } from "element-plus"
	import fetectEventRequest from '@/utils/fetchEvent'
	import { copy } from '@/utils/string'
	import { useCommonStore } from '@/store/common'
	import { v4 as uuidv4 } from 'uuid'
	import TimeUtils from '@/utils/time'
	//store
	const commonStore = useCommonStore()

	interface ChatMessage {
		text : string,
		// 0用户 1ai模型
		source : 0 | 1,
	}
	interface ChatSession {
		text : string,
		sessionId : string,
		time : string
	}
	//ai问答模型流式返回消息
	let modelReply : ChatMessage = null
	// 打开菜单
	const isOpenNav = ref(false)
	// 输入内容
	const inputRef = ref(null)
	const inputContent = ref("")
	const chatHistoryContainerRef = ref(null)
	const waitModelReply = ref(false)
	// 聊天记录
	const chatHistory = reactive<ChatMessage[]>([])
	const chatSessions = reactive<ChatSession[]>([])
	// 设置
	const settingData = reactive({
		dialogShow: false,
		modelDailogShow: false,
		defaultVendor: 'zpqy',
		form: {
			vendor: 'zpqy',
			modelConfig: {
				zpqy: {
					modelName: 'glm-4-flash',
					secretKey: '',
					stream: true,
					refContext: true,
					webSearch: false
				},
				tyqw: {
					modelName: 'tyqw',
					appId: '',
					appSecret: '',
					stream: true
				}
			},
			
		},
		data: null,
		modelOptions:{
			zpqy: ["glm-4-flash"],
			tyqw: ["tyqw"]
		},
		vendorOptions: [{
			name: 'zpqy',
			label: '智谱清言',
			avatarUrl: "/images/aichat/zpqy.png"
		},{
			name: 'tyqw',
			label: '通义千问',
			avatarUrl: "/images/aichat/tyqw.png"
		}]
		
	})

	const data = reactive({
		// 用户信息
		userInfo: {
			name: 'user',
			label: "用户",
			avatarUrl: "/svg/default_avatar.svg"
		},
		// ai问答模型信息
		modelInfo: settingData.vendorOptions[0],
		// 会话id
		sessionId: null,
		example: [
			"讲个笑话", "用java实现1-100的累加运算"
		]
	})
	
	// 点击示例
	const submitExample = (event:any) => {
		submitMessage(event?.target?.innerText) 
	}

	//提交信息
	const submitMessage = (message:string) => {
		if (!message) {
			ElMessage.error('发送内容不能为空')
			return
		}
		if (!data.sessionId) {
			// 保存新会话
			data.sessionId = uuidv4()
			chatSessions.unshift({text: message, sessionId: data.sessionId, time: TimeUtils.Jh_timeStampToTime(new Date().getTime(), '{y}-{m}-{d} {h}:{i}:{s}')})
			localStorage.setItem('chatSessions', JSON.stringify(chatSessions))
		}
		waitModelReply.value = true
		// 清空输入，并记录用户发送消息
		inputContent.value = ""
		let chatMsgItem : ChatMessage = {
			text: message,
			source: 0,
		}
		chatHistory.push(chatMsgItem)
		localStorage.setItem('chatHistrory_' + data.sessionId, JSON.stringify(chatHistory))
		scrollToChatBottom()
		// 清空ai问答模型消息缓存
		modelReply = null
		// 等待ai问答模型返回
		connectSse(message)
	}

	// 实现对话时保持滚动在底部
	const scrollToChatBottom = () => {
		const container = chatHistoryContainerRef.value;
		// 滚动到最底部
		setTimeout(() => {
			container.scrollTo({
			  top: container.scrollHeight,
			  behavior: 'smooth'
			});
		  //container.scrollTop = container.scrollHeight;
		}, 100);
	}
	// 复制代码
	const handleCopyCodeSuccess = (code : string) => {
		copy(code)
	}

	// sse连接
	var eventSource : any = null;
	// 中断请求
	var ctrl : AbortController = null;
	class RetriableError extends Error { }
	class FatalError extends Error { }
	const connectSse = (message : string) => {
		if (eventSource) {
			closeSse();
		}
		ctrl = new AbortController()
		eventSource = fetectEventRequest('/chat/sse', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
			},
			body: JSON.stringify({
				sessionId: data.sessionId,
				message: message,
				vendor: settingData.data?.vendor,
				config: settingData.data?.modelConfig[settingData.data.vendor]
			}),
			signal: ctrl.signal,
			async onopen(response) {
				if (response.ok && response.headers.get('content-type') === 'text/event-stream') {
					console.log('连接已打开');
					return; // everything's good
				} else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
					// client-side errors are usually non-retriable:
					throw new FatalError();
				} else {
					throw new RetriableError();
				}
			},
			onmessage(msg) {
				if (modelReply == null) {
					// 模型回复消息
					modelReply = {
						text: '',
						source: 1,
					}
					chatHistory.push(modelReply)
					scrollToChatBottom()
				}
				if (msg.event === 'message') {
					chatHistory[chatHistory.length - 1].text += msg.data.replace(/\\\\n/g, '\n').replace(/{{SPACE}}/g, ' ');
					if (msg.data.indexOf('\\\\n') !== -1) {
						scrollToChatBottom()
					}
				} else if (msg.event === 'finish') {
					waitModelReply.value = false
					localStorage.setItem('chatHistrory_' + data.sessionId, JSON.stringify(chatHistory))
				}
			},
			onclose() {
				// if the server closes the connection unexpectedly, retry:
				//throw new RetriableError();
				console.log("sse onClose")
				waitModelReply.value = false
			},
			onerror(err: any) {
				console.log(err)
				ctrl.abort()
				waitModelReply.value = false
				ElMessage.error('连接异常')
				if (err instanceof FatalError) {
					throw err; // rethrow to stop the operation
				} else {
					// do nothing to automatically retry. You can also
					// return a specific retry interval here.
					throw err;
				}
			},
			// 默认为false，在文档再次变为可见时自动关闭请求并重新打开，导致莫名奇怪的断连和重连
			openWhenHidden: true
		})
	}
	// 断开sse连接
	const closeSse = () => {
		if (eventSource) {
			ctrl.abort();
			eventSource = null;
		}
	}
	// 创建新会话
	const createNewSession = () => {
		isOpenNav.value = false
		if (!data.sessionId) {
			ElMessage('已切换最新会话')
			return
		}
		data.sessionId = null
		chatHistory.length = 0
	}
	// 加载会话
	const loadChatSession = ()=>{
		//取出缓存会话
		const storedChatSessions = localStorage.getItem('chatSessions');
		if (storedChatSessions) {
			chatSessions.push(...JSON.parse(storedChatSessions));
		}
		if (chatSessions.length > 0) {
			data.sessionId = chatSessions[0].sessionId
		}
		loadChatHistory(data.sessionId)
		scrollToChatBottom()
	}
	const loadChatHistory = (targetSessionId:string)=>{
		chatHistory.length = 0
		const storedChatHistory = localStorage.getItem('chatHistrory_' + targetSessionId);
		if (storedChatHistory) {
			chatHistory.push(...JSON.parse(storedChatHistory));
		}
	}
	// 切换会话
	const changeSession = (targetSessionId:string)=>{
		isOpenNav.value = false
		if (targetSessionId == data.sessionId) {
			return;
		}
		data.sessionId = targetSessionId
		loadChatHistory(targetSessionId)
		scrollToChatBottom()
	}
	// 删除会话
	const deleteSession= (targetSessionId:string)=>{
		data.sessionId = null;
		for (let i = chatSessions.length - 1; i >= 0; i--) {
		    if (chatSessions[i].sessionId === targetSessionId) {
		        chatSessions.splice(i, 1);
		    }
		}
		localStorage.setItem('chatSessions', JSON.stringify(chatSessions))
		localStorage.removeItem('chatHistrory_' + targetSessionId)
	}
	
	const changeVendor = ()=>{
		
	}
	
	const loadChatSetting = ()=>{
		let localChatSetting = localStorage.getItem('chatSetting')
		if (localChatSetting) {
			settingData.data = JSON.parse(localChatSetting)
			settingData.form = settingData.data
		} else {
			settingData.data = settingData.form;
		}
		
		let vendor = settingData.data?.vendor || settingData.defaultVendor;
		data.modelInfo = settingData.vendorOptions.find(v => v.name === vendor)
	}
	//保存设置
	const submitSetting=()=>{
		settingData.data = settingData.form
		localStorage.setItem('chatSetting', JSON.stringify(settingData.data))
		
		let vendor = settingData.data?.vendor || settingData.defaultVendor;
		data.modelInfo = settingData.vendorOptions.find(v => v.name === vendor)
		settingData.dialogShow = false
	}
	
	onMounted(() => {
		inputRef.value.focus()
		// 加载会话列表
		loadChatSession()
		// 加载设置
		loadChatSetting()
	})
</script>

<style>
	#chat-history-container::-webkit-scrollbar {
		width: 0px;
		height: 0px;
	}
	.session-active {
		border-color: var(--el-color-primary);
		border-left-width: 4px;
		background-color: var(--el-color-primary-light-9);
	}
	
	.session-item:hover {
		background-color: var(--el-color-primary-light-9);
		.hover-show {
			display: block;
		}
	}
	.hover-show:hover {
		color: red
	}
	.hover-primary:hover {
		color: var(--el-color-primary);
	}
	.text-primary {
		color: var(--el-color-primary);
	}
	.expand-btn {
		width: 30px;
		height: 30px;
		border-radius: 0 50% 50% 0;
		border-width: 2px;
		border-color: inherit;
		position: fixed;
		left: -12px;
		top: 50%;
		cursor: pointer;
		z-index: 1000; /* 确保按钮在顶部 */
	}
</style>